package netty.rpc.core;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import netty.rpc.register.Notify;
import netty.rpc.register.Register;
import netty.rpc.register.RegisterProperties;
import netty.rpc.register.RegisterService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.omg.CORBA.PRIVATE_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 服务注册表
 * @author Jim
 * @version 1.0.0
 * @descripition:
 * @date 2022/5/21 14:26
 */
public class ServiceRegistry {

    public static final Logger logger = LogManager.getLogger(ServiceRegistry.class);

    /**
     * 服务定义列表
     */
    private final List<ServiceDefinition> serviceList = new CopyOnWriteArrayList<>();

    private final Map<String,List<String>> instanceAddressMap = new HashMap<>();

    private RegisterService registerService;

    public void init(RegisterProperties registerProperties){
        try {
            registerService.init(registerProperties);
        } catch (Exception e) {
            logger.error("registerService init error.",e);
            System.exit(-1);
        }
    }

    private ServiceRegistry(){
        // 注册中心实现
        ServiceLoader<RegisterService> registerServices = ServiceLoader.load(RegisterService.class);
        for (RegisterService registerService:registerServices){
            this.registerService = registerService;
        }
    }

    /**
     * 随机选择 服务实例
     * @param serviceId
     * @return
     */
    public String luckyPick(String serviceId) {
        synchronized (ServiceRegistry.class){
            List<String> instanceAddresses = instanceAddressMap.get(serviceId);
            if (null == instanceAddresses || instanceAddresses.size() == 0){
                return null;
            }
            return instanceAddresses.get(new Random().nextInt(instanceAddresses.size()));
        }
    }

    static class Singleton{
        private static final ServiceRegistry INSTANCE = new ServiceRegistry();
    }

    public static ServiceRegistry getInstance(){
        return Singleton.INSTANCE;
    }



    /**
     * 服务注册
     * @param serviceDefinition 服务定义
     *                          /simple-netty-rpc-dev
     * 	/services
     * 		/serviceA:1.0.0 ServiceDefinition
     * 			/interfaceA InterfaceDefinition
     * 			/interfaceB InterfaceDefinition
     * 		/serviceB:1.0.1
     * 	/instances
     * 		/serviceA:1.0.0
     * 			/192.168.0.15:8077
     * 			/192.168.1.32:8099
     * 		/serviceB:1.0.1
     * 			/192.168.3.15:8077
     * 			/192.168.4.32:8099
     *
     */
    public void serviceRegister(ServiceDefinition serviceDefinition) throws Exception{

        // 注册服务
        registerService.registerPathIfNotExists(Register.PATH+"-"+serviceDefinition.getEnvType()+Register.SERVICE_PREFIX+"/"+serviceDefinition.getServiceId(),JSON.toJSONString(serviceDefinition.getInterfaceSet()),true);
        logger.info("service {} register path:{},data:{}",serviceDefinition.getServiceName(),Register.PATH+"-"+serviceDefinition.getEnvType()+Register.SERVICE_PREFIX+"/"+serviceDefinition.getServiceId(),JSON.toJSONString(serviceDefinition));
        // 注册服务下接口
        Set<InterfaceDefinition> interfaceSet = serviceDefinition.getInterfaceSet();

        for (InterfaceDefinition interfaceDefinition:interfaceSet){
            registerService.registerPathIfNotExists(Register.PATH+"-"+serviceDefinition.getEnvType()+Register.SERVICE_PREFIX+"/"+serviceDefinition.getServiceId()+"/"+interfaceDefinition.getInterfaceName(),JSON.toJSONString(interfaceDefinition.getMethodDefinitionList()),true);
            logger.info("interface {} register path:{}",interfaceDefinition.getInterfaceName(),Register.PATH+"-"+serviceDefinition.getEnvType()+Register.SERVICE_PREFIX+"/"+serviceDefinition.getServiceId()+"/"+interfaceDefinition.getMethodDefinitionList());
        }
        // 注册Instance的上层目录
        registerService.createDirector(Register.PATH+"-"+serviceDefinition.getEnvType()+Register.INSTANCE_PREFIX+"/"+serviceDefinition.getServiceId(), true);
        logger.info("persistent service {} instance construct path:{}",serviceDefinition.getServiceName(),Register.PATH+"-"+serviceDefinition.getEnvType()+Register.INSTANCE_PREFIX+"/"+serviceDefinition.getServiceId());

    }

    /**
     * 监听服务变化
     * @param envType
     */
    public void listenOnServiceChange(String envType) throws Exception{
        // 监听服务定义变化
        registerService.addWatcherListener(Register.PATH + "-" + envType + Register.SERVICE_PREFIX, new ServicesChangeNotify());
        logger.info("listen on service definition change path:{}",Register.PATH + "-" + envType + Register.SERVICE_PREFIX);
        // 监听实例变化
        for (ServiceDefinition item:serviceList){
            registerService.addWatcherListener(Register.PATH + "-" + envType + Register.INSTANCE_PREFIX+"/"+item.getServiceId(), new ServiceInstanceChangeNotify());
            logger.info("listen on service instance change path:{}",Register.PATH + "-" + envType + Register.INSTANCE_PREFIX+"/"+item.getServiceId());
        }

    }






    /**
     * 服务实例注册
     * @param serviceInstance
     */
    public void serviceInstanceRegister(String envType,ServiceInstance serviceInstance) throws Exception {
       //TODO:服务实例的结构不是很好 后续优化
        registerService.registerEphemeralNode(Register.PATH+"-"+envType+Register.INSTANCE_PREFIX+"/"+serviceInstance.getServiceId()+"/"+serviceInstance.getIp()+":"+serviceInstance.getPort());
        logger.info("service {} instance {} register path:{}",serviceInstance.getServiceName(),Register.PATH+"-"+envType+Register.INSTANCE_PREFIX+"/"+serviceInstance.getServiceId()+"/"+serviceInstance.getIp()+":"+serviceInstance.getPort());
    }

    /**
     * 拉取注册表
     */
    public void pullServices(String env) throws Exception{
        String path = Register.PATH+"-"+env+Register.SERVICE_PREFIX;
        List<String> services = registerService.getChildrenDirectors(path);

        for (String serviceId:services){
            String interfaceDefinitionSet = registerService.getServiceDefinition(env,serviceId);
            List<InterfaceDefinition> interfaceDefinitionList = JSON.parseArray(interfaceDefinitionSet).toList(InterfaceDefinition.class);
            List<String> instanceAddresses = registerService.getServiceInstance(env, serviceId);
            synchronized (ServiceRegistry.class){
                instanceAddressMap.put(serviceId, instanceAddresses);
            }
        }
        logger.info("pull registry from register center path:{},services:{}, instances address:{}",path,serviceList,instanceAddressMap);
    }



    /**
     * 服务更新Notify
     */
    class ServicesChangeNotify implements Notify{

        /**
         * 有新的服务加入
         * @param path 服务名
         * @param value 接口定义集合
         * @throws Exception
         */
        @Override
        public void onAdd(String path, String value) throws Exception {
            logger.error("service notify on add path:{},value:{}",path,value);
            ServiceDefinition serviceDefinition = new ServiceDefinition();
            serviceDefinition.setServiceId(path.split("/")[path.split("/").length-1]);
            List<InterfaceDefinition> interfaceDefinitions = JSON.parseArray(value).toList(InterfaceDefinition.class);
            for (InterfaceDefinition item:interfaceDefinitions){
                serviceDefinition.getInterfaceSet().add(item);
            }
            serviceList.add(serviceDefinition);
            String instancePath = path.replace(Register.SERVICE_PREFIX, Register.INSTANCE_PREFIX);
            // 注册实例更新监听
            registerService.addWatcherListener(instancePath, new ServiceInstanceChangeNotify());
        }

        @Override
        public void onUpdate(String path, String value) throws Exception {
            logger.info("service notify on update path:{},value:{}",path,value);
            List<InterfaceDefinition> interfaceDefinitions = JSON.parseArray(value).toList(InterfaceDefinition.class);

            String serviceId = path.split("/")[path.split("/").length - 1];
            for (ServiceDefinition item:serviceList){
                if (item.getServiceId().equals(serviceId)){
                    item.getInterfaceSet().clear();
                    for (InterfaceDefinition interfaceDefinition:interfaceDefinitions){
                        item.getInterfaceSet().add(interfaceDefinition);
                    }
                }
            }
        }

        @Override
        public void onDelete(String path) throws Exception {
            //TODO 服务下线、宕机等触发，判断服务是否还有实例存在，如果没有，删除服务
        }
    }

    /**
     * 实例更新Notify
     */
    class ServiceInstanceChangeNotify implements Notify{

        @Override
        public void onAdd(String path, String value) throws Exception {
            logger.info("service instance notify on add path:{},value:{}",path,value);
            String serviceId = path.split("/")[path.split("/").length-2];
            String instanceAddress = path.split("/")[path.split("/").length-1];
            synchronized (ServiceRegistry.class){
                instanceAddressMap.putIfAbsent(serviceId, new ArrayList<>());
                instanceAddressMap.get(serviceId).add(instanceAddress);
            }
        }

        @Override
        public void onUpdate(String path, String value) throws Exception {
            // 不处理

        }

        @Override
        public void onDelete(String path) throws Exception {
            logger.info("service instance notify on delete path:{}",path);
            String serviceId = path.split("/")[path.split("/").length-2];
            String instanceAddress = path.split("/")[path.split("/").length-1];
            synchronized (ServiceRegistry.class){
                instanceAddressMap.get(serviceId).remove(instanceAddress);
            }
        }
    }
}
